// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: lucasyoo

#include "coin2/strategy/hamm/hamm_pricer.h"

namespace coin2::strategy::hamm {

HammPricer::HammPricer(
    HammPricerConfig* config,
    double reserve,
    double lot_size,
    std::optional<double> lot_size_quote,
    double min_pos,
    double max_pos,
    int64_t strategy_created_time)
    : config_(*config),
      strategy_created_time_(strategy_created_time),
      init_base_qty_(config->init_base_qty()),
      target_ratio_(config->target_ratio()),
      gamma_(config->gamma()),
      kappa_(config->kappa()),
      is_closing_time_infinite_(config->is_closing_time_infinite()),
      closing_timestamp_(DatetimeToUnixTimestamp(config->closing_time())),
      min_spread_bps_(config->min_spread_bps()),
      max_spread_bps_(config->max_spread_bps()),
      price_type_(config->price_type()),
      spread_control_(config->spread_control()),
      min_max_control_(config->min_max_control()),
      fill_price_limit_(config->fill_price_limit_sec() * 1e9),
      trade_cnt_(config->trade_cnt()),
      full_book_num_(config->full_book_num()),
      single_amount_(config->single_amount()),
      total_amount_(config->total_amount()),
      min_pos_(min_pos),
      max_pos_(max_pos),
      lot_size_(lot_size),
      lot_size_quote_(lot_size_quote),
      q_method_(config->q_method()),
      fake_method_(config->fake_method()),
      fade_bps_(config->fade_bps()),
      fade_decay_sec_(config->fade_decay_sec()),
      fade_bps_multiplier_(config->fade_bps_multiplier()),
      fill_num_hurdle_(config->fill_num_hurdle()) {
  for (const auto& guard_method : config->guard_methods()) {
    guard_methods_.push_back(guard_method);
  }
  if (init_base_qty_ == 0.0) init_base_qty_ = reserve;
  vol_calc_ = std::make_unique<VolatilityCalc>(
      config->vol_window_sec(),
      config->vol_tick_intv_sec(),
      config->init_vol());
  trade_collector_ = std::make_unique<TradeCollector>(config->trade_window_sec());
  fill_collector_ = std::make_unique<TradeCollector>(
      config->fill_window_sec(),
      std::optional<double>(config->ewma_alpha()));
  midp_ma_collector_ = std::make_unique<MovingAverage>(config->trade_window_sec(), 0.0);
  full_book_midp_collector_ = std::make_unique<MovingAverage>(config->trade_window_sec(), 0.0);
  full_book_weighted_midp_collector_ =
      std::make_unique<MovingAverage>(config->trade_window_sec(), 0.0);
  eta2_ = config->eta2();
  auto* order_qty_generator_config = config->mutable_order_qty_generator_config();
  if (order_qty_generator_config->order_qty_mode() == "single") {
    order_qty_generator_ =
        std::make_unique<SingleOrderQtyGenerator>(*order_qty_generator_config, lot_size);
  } else if (order_qty_generator_config->order_qty_mode() == "multi") {
    order_qty_generator_ =
        std::make_unique<MultiOrderQtyGenerator>(*order_qty_generator_config, lot_size);
  }
  LOG(INFO) << "HammPricer created: " << config->DebugString();
}

void HammPricer::UpdateTrade(const FeedUpdate& upd) {
  int64_t ts = upd.timestamp();
  double p = upd.trade().price;
  double q = upd.trade().fill_qty;
  auto& trade_side = upd.trade().side;
  trade_collector_->UpdateTrade(p, q, ts, trade_side);
  vwap_ = trade_collector_->VWAP();
  buy_vwap_ = trade_collector_->BuyVWAP();
  sell_vwap_ = trade_collector_->SellVWAP();
  vol_intensity_ = trade_collector_->VolIntensity(trade_cnt_);
  ma_ = trade_collector_->MA();
}

void HammPricer::FullBookMidP(const FeedUpdate& upd) {
  const auto* book = upd.GetBookBuilder();
  double bid, ask;
  const int bid_size = std::min(full_book_num_, static_cast<int>(book->BidSize()));
  double bid_amount = 0.0;
  bid = book->Bid0()->price;
  for (int i = 0; i < bid_size; ++i) {
    double amt = book->BidN(i)->price * book->BidN(i)->qty;
    if (amt > single_amount_) {
      bid_amount += amt;
    }
    bid = book->BidN(i)->price;
    if (bid_amount >= total_amount_) {
      SPDLOG_INFO_EVERY_NS(
          60e9,
          upd.timestamp(),
          fmt::format("Passed {} Bids -> BidP: {:.8f}", i, bid));
      break;
    }
  }
  ask = book->Ask0()->price;
  const int ask_size = std::min(full_book_num_, static_cast<int>(book->AskSize()));
  double ask_amount = 0.0;
  for (int i = 0; i < ask_size; ++i) {
    double amt = book->AskN(i)->price * book->AskN(i)->qty;
    if (amt > single_amount_) {
      ask_amount += amt;
    }
    ask = book->AskN(i)->price;
    if (ask_amount >= total_amount_) {
      SPDLOG_INFO_EVERY_NS(
          60e9,
          upd.timestamp(),
          fmt::format("Passed {} Asks -> AskP: {:.8f}", i, ask));
      break;
    }
  }
  fake_out_fb_bid_ = bid;
  fake_out_fb_ask_ = ask;
}

void HammPricer::FullBookWeightedMidP(const FeedUpdate& upd) {
  const auto* book = upd.GetBookBuilder();
  double bid_q_sum = 0.0;
  double bid_pq_sum = 0.0;
  const int bid_size = std::min(full_book_num_, static_cast<int>(book->BidSize()));
  for (int i = 0; i < bid_size; ++i) {
    double amt = book->BidN(i)->price * book->BidN(i)->qty;
    bid_q_sum += book->BidN(i)->qty;
    bid_pq_sum += amt;
    if (bid_pq_sum > total_amount_) {
      SPDLOG_INFO_EVERY_NS(
          60e9,
          upd.timestamp(),
          fmt::format("Passed {} Bids -> BidP: {:.8f}", i, book->BidN(i)->price));
      break;
    }
  }
  double ask_q_sum = 0.0;
  double ask_pq_sum = 0.0;
  const int ask_size = std::min(full_book_num_, static_cast<int>(book->AskSize()));
  for (int i = 0; i < ask_size; ++i) {
    double amt = book->AskN(i)->price * book->AskN(i)->qty;
    ask_q_sum += book->AskN(i)->qty;
    ask_pq_sum += amt;
    if (ask_pq_sum > total_amount_) {
      SPDLOG_INFO_EVERY_NS(
          60e9,
          upd.timestamp(),
          fmt::format("Passed {} Asks -> AskP: {:.8f}", i, book->AskN(i)->price));
      break;
    }
  }
  double weighted_bid = bid_pq_sum / bid_q_sum;
  double weighted_ask = ask_pq_sum / ask_q_sum;

  fake_out_fbw_bid_ = weighted_bid;
  fake_out_fbw_ask_ = weighted_ask;
}

double HammPricer::CalculateQ() {
  if (q_method_ == "normal") {
    return (base_qty_ - init_base_qty_) / init_base_qty_;
  } else if (q_method_ == "clipped") {
    double max_pos =
        lot_size_quote_.has_value() && midp_ != 0.0
            ? std::min(
                  (max_pos_ - init_base_qty_) * lot_size_quote_.value() / (midp_ * lot_size_) +
                      init_base_qty_,
                  max_pos_)
            : max_pos_;
    double min_pos = init_base_qty_ - (init_base_qty_ - min_pos_) / (max_pos_ - init_base_qty_) *
                                          (max_pos - init_base_qty_);
    if (lot_size_quote_.has_value() && midp_ != 0.0) {
      clipped_min_pos_ = min_pos;
      clipped_max_pos_ = max_pos;
      clipped_lot_size_ = std::min(lot_size_quote_.value(), lot_size_ * midp_) / midp_;
    }
    return (base_qty_ > init_base_qty_)
               ? (max_pos > base_qty_ ? (base_qty_ - init_base_qty_) / (max_pos - init_base_qty_)
                                      : 1)
               : (min_pos < base_qty_ ? (base_qty_ - init_base_qty_) / (init_base_qty_ - min_pos)
                                      : -1);

  } else {
    NOTREACHED() << "Unknown mode: " << q_method_;
  }
}

void HammPricer::TotalGuardClipping() {
  std::ostringstream ss;
  ss << "Priced Optimal Spread: " << optimal_bid_ask_.first << " " << optimal_bid_ask_.second;
  for (const auto& guard : guard_methods_) {
    ss << fmt::format("\n{}: ", guard);
    if (guard == "MinMaxSpread") {
      MinMaxSpreadClipping();
    } else if (guard == "ArbFree") {
      ArbFreeBboClipping();
    } else if (guard == "FillTimeLimit") {
      FillTimeLimitClipping();
    } else if (guard == "FillDecay") {
      FillDecayClipping();
    }
    ss << fmt::format(
        "optimal_bid_ask_: {:.8f}, {:.8f}",
        optimal_bid_ask_.first,
        optimal_bid_ask_.second);
  }
  LOG(INFO) << ss.str();
}

void HammPricer::MinMaxSpreadClipping() {
  double guard_price = midp_;

  if (min_max_control_ == "midp") {
    guard_price = midp_;
  } else if (min_max_control_ == "reservation") {
    guard_price = reservation_price_;
  } else {
    guard_price = price_;
  }

  double high_price = std::max(midp_, guard_price);
  double low_price = std::min(midp_, guard_price);

  double ub_bidp = std::min({low_price * (1.0 - min_spread_bps_ * 1e-4), buy_vwap_});
  double lb_bidp = std::min({low_price * (1.0 - max_spread_bps_ * 1e-4), buy_vwap_});
  double ub_askp = std::max({high_price * (1.0 + max_spread_bps_ * 1e-4), sell_vwap_});
  double lb_askp = std::max({high_price * (1.0 + min_spread_bps_ * 1e-4), sell_vwap_});

  optimal_bid_ask_ = std::make_pair(
      std::max(std::min(optimal_bid_ask_.first, ub_bidp), lb_bidp),
      std::min(std::max(optimal_bid_ask_.second, lb_askp), ub_askp));
}

void HammPricer::ArbFreeBboClipping() {
  // Check arb_free_bbo and adjust final bbo
  if (arb_free_bbo_.first != 0.0 || arb_free_bbo_.second != 0.0) {
    if (is_adjust_.first)
      optimal_bid_ask_.first = std::min(arb_free_bbo_.first, optimal_bid_ask_.first);
    if (is_adjust_.second)
      optimal_bid_ask_.second = std::max(arb_free_bbo_.second, optimal_bid_ask_.second);
  }
}

void HammPricer::FillTimeLimitClipping() {
  fill_collector_->UpdateTimeOnly(timestamp_);
  buy_filled_vwap_ = fill_collector_->BuyVWAP();
  buy_filled_vwap_ = buy_filled_vwap_ == 0.0 ? buy_vwap_ : buy_filled_vwap_;
  sell_filled_vwap_ = fill_collector_->SellVWAP();
  sell_filled_vwap_ = sell_filled_vwap_ == 0.0 ? sell_vwap_ : sell_filled_vwap_;
  // Buy is filled -> Limit Sell Price
  if (filled_buy_ts_ != 0 && timestamp_ - filled_buy_ts_ < fill_price_limit_ &&
      filled_buy_price_ > 0.0) {
    // double smoothed_sell_price1 = LinearTimeDecay(
    //     static_cast<double>(fill_price_limit_),
    //     timestamp_ - filled_buy_ts_,
    //     (buy_filled_vwap_),
    //     sell_vwap_);
    // double smoothed_sell_price2 = LinearTimeDecay(
    //     static_cast<double>(fill_price_limit_),
    //     timestamp_ - filled_buy_ts_,
    //     buy_filled_vwap_,
    //     optimal_bid_ask_.second);
    double ewma_buy_vwap = fill_collector_->EWMABuyVWAP();
    if (IsZero(ewma_buy_vwap)) ewma_buy_vwap = buy_filled_vwap_;
    // double smoothed_sell_price1 = LinearTimeDecay(
    //     static_cast<double>(fill_price_limit_),
    //     timestamp_ - filled_buy_ts_,
    //     ewma_buy_vwap,
    //     optimal_bid_ask_.second);
    double smoothed_sell_price1 = ewma_buy_vwap;
    // optimal_bid_ask_.second =
    //     std::max({optimal_bid_ask_.second, smoothed_sell_price1, smoothed_sell_price2});
    optimal_bid_ask_.second = std::max({optimal_bid_ask_.second, smoothed_sell_price1});
  }
  // Sell is filled -> Limit Buy Price
  if (filled_sell_ts_ != 0 && timestamp_ - filled_sell_ts_ < fill_price_limit_ &&
      filled_sell_price_ > 0.0) {
    // double smoothed_buy_price1 = LinearTimeDecay(
    //     static_cast<double>(fill_price_limit_),
    //     timestamp_ - filled_sell_ts_,
    //     sell_filled_vwap_,
    //     buy_vwap_);
    // double smoothed_buy_price2 = LinearTimeDecay(
    //     static_cast<double>(fill_price_limit_),
    //     timestamp_ - filled_sell_ts_,
    //     sell_filled_vwap_,
    //     optimal_bid_ask_.first);
    double ewma_sell_vwap = fill_collector_->EWMASellVWAP();
    if (IsZero(ewma_sell_vwap)) ewma_sell_vwap = sell_filled_vwap_;
    // double smoothed_buy_price1 = LinearTimeDecay(
    //     static_cast<double>(fill_price_limit_),
    //     timestamp_ - filled_sell_ts_,
    //     ewma_sell_vwap,
    //     optimal_bid_ask_.first);
    double smoothed_buy_price1 = ewma_sell_vwap;
    optimal_bid_ask_.first = std::min({optimal_bid_ask_.first, smoothed_buy_price1});
  }
}

void HammPricer::FakePriceClipping() {
  if (fake_method_ == "FB") {
    if (fake_out_fb_bid_ > 0.0)
      optimal_bid_ask_.first = std::min(optimal_bid_ask_.first, fake_out_fb_bid_);
    if (fake_out_fb_ask_ > 0.0)
      optimal_bid_ask_.second = std::max(optimal_bid_ask_.second, fake_out_fb_ask_);

  } else if (fake_method_ == "FBW") {
    if (fake_out_fbw_bid_ > 0.0)
      optimal_bid_ask_.first = std::min(optimal_bid_ask_.first, fake_out_fbw_bid_);
    if (fake_out_fbw_ask_ > 0.0)
      optimal_bid_ask_.second = std::max(optimal_bid_ask_.second, fake_out_fbw_ask_);
  }
}

void HammPricer::FillDecayClipping() {
  fill_collector_->UpdateTimeOnly(timestamp_);
  // fade_bps_, fade_decay_sec_ seconds decay window
  auto bbo_fadep = fill_collector_->BboFadePrice(
      fade_bps_,
      fade_decay_sec_,
      clipped_min_pos_.has_value() ? clipped_min_pos_.value() : min_pos_,
      clipped_max_pos_.has_value() ? clipped_max_pos_.value() : max_pos_,
      clipped_lot_size_.has_value() ? clipped_lot_size_.value() : lot_size_,
      init_base_qty_,
      base_qty_,
      fade_bps_multiplier_,
      fill_num_hurdle_);
  if (bbo_fadep.first > 0) {
    LOG(INFO) << "optimal_bid_ask_.first: " << optimal_bid_ask_.first
              << " bbo_fadep.first: " << bbo_fadep.first
              << " BuyElapsedSec: " << fill_collector_->BuyElapsedSec();
    optimal_bid_ask_.first = std::min(
        optimal_bid_ask_.first,
        LinearTimeDecay(
            fade_decay_sec_,
            std::min(fill_collector_->BuyElapsedSec(), fade_decay_sec_),
            bbo_fadep.first,
            optimal_bid_ask_.first));
  }
  if (bbo_fadep.second > 0) {
    LOG(INFO) << "optimal_bid_ask_.second: " << optimal_bid_ask_.second
              << " bbo_fadep.second: " << bbo_fadep.second
              << " SellElapsedSec: " << fill_collector_->SellElapsedSec();
    optimal_bid_ask_.second = std::max(
        optimal_bid_ask_.second,
        LinearTimeDecay(
            fade_decay_sec_,
            std::min(fill_collector_->SellElapsedSec(), fade_decay_sec_),
            bbo_fadep.second,
            optimal_bid_ask_.second));
  }
  LOG(INFO) << "optimal_bid_ask_: " << optimal_bid_ask_.first << " " << optimal_bid_ask_.second;
}

// Update (1) Optimal Spread (2) Reservation Price (3) Order Qty
void HammPricer::onBookFeed(const FeedUpdate& upd) {
  timestamp_ = upd.timestamp();
  if (base_qty_ == 0.0) {
    LOG(INFO) << "base_qty_ == 0.0";
    return;
  }
  double q = CalculateQ();
  // Update Volatility
  vol_calc_->UpdatePrice(midp_, timestamp_);
  volatility_ = vol_calc_->Volatility();

  if (volatility_ == 0.0) return;
  if (vwap_ == 0.0) vwap_ = midp_;
  if (buy_vwap_ == 0.0) buy_vwap_ = bbo_.first;
  if (sell_vwap_ == 0.0) sell_vwap_ = bbo_.second;
  if (ma_ == 0.0) ma_ = midp_;
  // Check time_left_fraction
  double time_left_fraction;
  if (!is_closing_time_infinite_) {
    time_left_fraction = 1;
  } else {
    time_left_fraction =
        (closing_timestamp_ - timestamp_) / (closing_timestamp_ - strategy_created_time_);
  }

  // Update min/less
  if (fake_method_ == "FB") {
    FullBookMidP(upd);
  } else if (fake_method_ == "FBW") {
    FullBookWeightedMidP(upd);
  }

  // Reservation Price and Optimal Spread
  if (price_type_ == "VWAP") {
    price_ = vwap_;
  } else if (price_type_ == "MA") {
    price_ = ma_;
  } else if (price_type_ == "MA2") {
    midp_ma_collector_->UpdateData(midp_, timestamp_);
    price_ = midp_ma_collector_->MA();
  } else if (price_type_ == "MIDP") {
    price_ = midp_;
  } else if (price_type_ == "FULL_BOOK_WEIGHTED_MIDP") {
    if (fake_method_ != "FBW") {
      FullBookWeightedMidP(upd);
    }
    price_ = (fake_out_fbw_bid_ + fake_out_fbw_ask_) / 2.0;
  } else if (price_type_ == "FULL_BOOK_MIDP") {
    if (fake_method_ != "FB") {
      FullBookMidP(upd);
    }
    price_ = (fake_out_fb_bid_ + fake_out_fb_ask_) / 2.0;
  } else if (price_type_ == "FULL_BOOK_WEIGHTED_MIDP_MA") {
    if (fake_method_ != "FBW") {
      FullBookWeightedMidP(upd);
    }
    price_ = (fake_out_fbw_bid_ + fake_out_fbw_ask_) / 2.0;
    full_book_weighted_midp_collector_->UpdateData(price_, timestamp_);
    double ma_price = full_book_weighted_midp_collector_->MA();
    if (ma_price != 0.0) price_ = ma_price;
  } else if (price_type_ == "FULL_BOOK_MIDP_MA") {
    if (fake_method_ != "FB") {
      FullBookMidP(upd);
    }
    price_ = (fake_out_fb_bid_ + fake_out_fb_ask_) / 2.0;
    full_book_midp_collector_->UpdateData(price_, timestamp_);
    double ma_price = full_book_weighted_midp_collector_->MA();
    if (ma_price != 0.0) price_ = ma_price;
  } else {
    NOTREACHED() << "Unknown price type: " << price_type_;
  }

  reservation_price_ = (price_ * (1 - q * gamma_ * volatility_ * volatility_ * time_left_fraction));

  optimal_spread_ = price_ * gamma_ * volatility_ * volatility_ * time_left_fraction;
  optimal_spread_ += 2 * price_ * std::log(1 + gamma_ / kappa_) / gamma_;

  double bid_spread = optimal_spread_ / 2.0;
  double ask_spread = optimal_spread_ / 2.0;
  if (spread_control_ != "NO") {
    if (spread_control_.find("MOMENTUM") != std::string::npos) {
      if (vol_intensity_ != -1 && vol_intensity_ > 0.5) {
        double vol_int_diff = vol_intensity_ - 0.5;
        ask_spread *= (1.0 / 0.5) * vol_int_diff + 1;
        if (spread_control_ == "MOMENTUM_ACTIVE") bid_spread *= (-0.5 / 0.5) * vol_int_diff + 1;
      } else if (vol_intensity_ != -1 && vol_intensity_ < 0.5) {
        double vol_int_diff = 0.5 - vol_intensity_;
        bid_spread *= (1.0 / 0.5) * vol_int_diff + 1;
        if (spread_control_ == "MOMENTUM_ACTIVE") ask_spread *= (-0.5 / 0.5) * vol_int_diff + 1;
      }
    } else if (spread_control_.find("REVERSION") != std::string::npos) {
      if (vol_intensity_ != -1 && vol_intensity_ > 0.5) {
        double vol_int_diff = vol_intensity_ - 0.5;
        bid_spread *= (1.0 / 0.5) * vol_int_diff + 1;
        if (spread_control_ == "REVERSION_ACTIVE") ask_spread *= (-0.5 / 0.5) * vol_int_diff + 1;
      } else if (vol_intensity_ != -1 && vol_intensity_ < 0.5) {
        double vol_int_diff = 0.5 - vol_intensity_;
        ask_spread *= (1.0 / 0.5) * vol_int_diff + 1;
        if (spread_control_ == "REVERSION_ACTIVE") bid_spread *= (-0.5 / 0.5) * vol_int_diff + 1;
      }
    }
  }
  if (q > 0) {
    bid_spread *= std::exp(q * eta2_);
  } else {
    ask_spread *= std::exp(-q * eta2_);
  }

  optimal_bid_ask_ =
      std::make_pair(reservation_price_ - bid_spread, reservation_price_ + ask_spread);

  // Guarding
  TotalGuardClipping();

  LOG(INFO) << "\n################################################"
            << fmt::format(
                   "\nMIDP: {:.8f}, RESERVATION_PRICE: {:.8f}, VOLATILITY: {:.8f}\n"
                   "MA: {:.8f}, VWAP: {:.8f}, BUY_VWAP: {:.8f} SELL_VWAP: {:.8f}\n"
                   "Q: {:.8f}, VOL_INTENSITY: {:.2f}\n"
                   "OPTIMAL_SPREAD: {:.8f}, BID_SPREAD: {:.8f}, ASK_SPREAD: {:.8f}\n",
                   midp_,
                   reservation_price_,
                   volatility_,
                   ma_,
                   vwap_,
                   buy_vwap_,
                   sell_vwap_,
                   q,
                   vol_intensity_,
                   optimal_spread_,
                   bid_spread,
                   ask_spread)
            << fmt::format(
                   "{} {}-{} PRICING RESULT OPTIMAL BID: {:.8f}({:.2f}bp), ASK: {:.8f}({:.2f}bp)",
                   upd.timestamp(),
                   upd.product().exchange_name(),
                   upd.product().absolute_norm(),
                   optimal_bid_ask_.first,
                   1e4 * (optimal_bid_ask_.first - midp_) / midp_,
                   optimal_bid_ask_.second,
                   1e4 * (optimal_bid_ask_.second - midp_) / midp_)
            << "\n################################################";
  order_qty_generator_->onBookFeed(
      q,
      optimal_bid_ask_,
      fill_collector_.get(),
      clipped_lot_size_.has_value() ? clipped_lot_size_.value() : lot_size_,
      fill_num_hurdle_);
}

// Update bid0/ask0/midp
void HammPricer::UpdateMidP(const FeedUpdate& upd) {
  const auto* book = upd.GetBookBuilder();
  if (book->AskSize() == 0 || book->BidSize() == 0) return;
  auto ask0 = book->Ask0();
  auto bid0 = book->Bid0();
  bbo_ = std::make_pair(bid0->price, ask0->price);
  midp_ = (bbo_.first + bbo_.second) * 0.5;
  LOG(INFO) << fmt::format(
      "{} UPDATE_MIDP {}-{} BID0: {:.8f}, ASK0: {:.8f}, MIDP: {:.8f}, BIDSIZE: {}, ASKSIZE: {}",
      upd.timestamp(),
      upd.product().exchange_name(),
      upd.product().absolute_norm(),
      bbo_.first,
      bbo_.second,
      midp_,
      book->BidSize(),
      book->AskSize());
}

// Update Pos
void HammPricer::UpdatePos(double pos) { base_qty_ = pos; }

void HammPricer::SetArbFreeBbo(std::pair<double, double> bbo, std::pair<bool, bool> is_adjust) {
  arb_free_bbo_ = bbo;
  is_adjust_ = is_adjust;
}

void HammPricer::ResetArbFreeBbo() {
  arb_free_bbo_ = std::make_pair(0.0, 0.0);
  is_adjust_ = std::make_pair(false, false);
}

void HammPricer::UpdateFillInfo(double p, double q, int64_t ts, std::string side_str) {
  if (side_str == "BUY") {
    filled_buy_price_ = p;
    filled_buy_ts_ = ts;
    fill_collector_->UpdateTrade(p, q, ts, coin2::exchange::feed::TRADE_BUY_SIDE);
  } else {
    filled_sell_price_ = p;
    filled_sell_ts_ = ts;
    fill_collector_->UpdateTrade(p, q, ts, coin2::exchange::feed::TRADE_SELL_SIDE);
  }
}

}  // namespace coin2::strategy::hamm